home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0-b / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltDragDrop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-19  |  72.2 KB  |  2,554 lines

  1. /*
  2.  * ------------------------------------------------------------------------
  3.  *  PURPOSE:  drag&drop widget registration facility
  4.  *
  5.  *  Allows widgets to be registered as drag&drop sources and targets
  6.  *  for handling "drag-and-drop" operations between Tcl/Tk applications.
  7.  *
  8.  *  USAGE:
  9.  *    blt_drag&drop source
  10.  *    blt_drag&drop source <pathName>
  11.  *    blt_drag&drop source <pathName> config ?options...?
  12.  *    blt_drag&drop source <pathName> handler ?<dataType> <cmd>...?
  13.  *
  14.  *    blt_drag&drop target
  15.  *    blt_drag&drop target <pathName> handler
  16.  *    blt_drag&drop target <pathName> handler ?<dataType> <cmd>...?
  17.  *    blt_drag&drop target <pathName> handle <dataType>
  18.  *
  19.  *    blt_drag&drop drag <pathName> <x> <y>
  20.  *    blt_drag&drop drop <pathName> <x> <y>
  21.  *
  22.  *    blt_drag&drop errors ?<proc>?
  23.  *    blt_drag&drop active
  24.  *    blt_drag&drop location ?<x> <y>?
  25.  *
  26.  * ------------------------------------------------------------------------
  27.  *  AUTHOR:  Michael J. McLennan       Phone: (215)770-2842
  28.  *           AT&T Bell Laboratories   E-mail: aluxpo!mmc@att.com
  29.  *
  30.  *     RCS:  bltDragDrop.c,v 1.10 1994/04/14 21:11:03 gah Exp
  31.  * ========================================================================
  32.  *                 Copyright (c) 1993-1994  AT&T Bell Laboratories
  33.  * ========================================================================
  34.  * Permission to use, copy, modify, and distribute this software and its
  35.  * documentation for any purpose and without fee is hereby granted,
  36.  * provided that the above copyright notice appear in all copies and that
  37.  * both that the copyright notice and warranty disclaimer appear in
  38.  * supporting documentation, and that the names of AT&T Bell Laboratories
  39.  * any of their entities not be used in advertising or publicity
  40.  * pertaining to distribution of the software without specific, written
  41.  * prior permission.
  42.  * 
  43.  * AT&T disclaims all warranties with regard to this software, including
  44.  * all implied warranties of merchantability and fitness.  In no event
  45.  * shall AT&T be liable for any special, indirect or consequential
  46.  * damages or any damages whatsoever resulting from loss of use, data or
  47.  * profits, whether in an action of contract, negligence or other
  48.  * tortuous action, arising out of or in connection with the use or
  49.  * performance of this software.
  50.  * ========================================================================
  51.  */
  52. #include "blt.h"
  53. #include <X11/Xatom.h>
  54.  
  55. #if (TK_MINOR_VERSION > 0)
  56. #define Cursor Tk_Cursor
  57. #endif
  58.  
  59. #ifndef DRAGDROP_VERSION
  60. #define DRAGDROP_VERSION "2.1"
  61. #endif
  62.  
  63. #define DRAGDROP_COMMAND    "blt_drag&drop"   /* Command name */
  64. #define DRAGDROP_CLASS        "DragDrop"        /* CLASS NAME for token window */
  65. #define DRAGDROP_PROPINFO    "BltDragDropInfo" /* Property name */
  66. #define DRAGDROP_ERRORPROC    "tkerror"          /* Error Proc used to report 
  67.                                * drag&drop background errors */
  68.  
  69. #define DEF_BUTTON_ACTIVE_BG_COLOR  BG2
  70. #define DEF_BUTTON_ACTIVE_BG_MONO   BLACK
  71. #define DEF_BUTTON_ACTIVE_FG_COLOR  BLACK
  72. #define DEF_BUTTON_ACTIVE_FG_MONO   WHITE
  73. #define DEF_BUTTON_BG_COLOR         BG2
  74. #define DEF_BUTTON_BG_MONO          WHITE
  75. #define DEF_TOKEN_OUTLINE_COLOR     BLACK
  76. #define DEF_TOKEN_OUTLINE_MONO      BLACK
  77.  
  78. /*
  79.  *  DRAG&DROP ROOT WINDOW HIERARCHY (cached during "drag" operations)
  80.  */
  81. typedef struct DD_WinRep {
  82.   Window win;            /* X window for this record */
  83.   int initialized;        /* non-zero => rest of info is valid */
  84.   int x0, y0;            /* upper-left corner of window */
  85.   int x1, y1;            /* lower-right corner of window */
  86.   char *ddprop;            /* drag&drop property info */
  87.   char *ddinterp;        /* interp name within ddprop */
  88.   char *ddwin;            /* target window name within ddprop */
  89.   char *ddhandlers;        /* list of handlers within ddprop */
  90.   struct DD_WinRep* parent;    /* window containing this as a child */
  91.   struct DD_WinRep* kids;    /* list of child windows */
  92.   struct DD_WinRep* next;    /* next sibling */
  93. } DD_WinRep;
  94.  
  95. /*
  96.  *  DRAG&DROP REGISTRATION DATA
  97.  */
  98. typedef struct {
  99.   Tcl_Interp *interp;        /* interpreter managing this drag&drop command */
  100.   Tk_Window root;        /* main window for application */
  101.   Tcl_HashTable srcList;    /* list of source widgets */
  102.   Tcl_HashTable trgList;    /* list of target widgets */
  103.   char *errorProc;        /* proc invoked for drag&drop errors */
  104.   int numactive;        /* number of active drag&drop operations */
  105.   int locx, locy;        /* last location point */
  106.   DD_WinRep *pool;        /* pool of available DD_WinRep records */
  107. } DD_RegList;
  108.  
  109. typedef struct {
  110.   DD_RegList *ddlist;        /* parent registration list */
  111.   Tk_Window tkwin;        /* registered window */
  112. } DD_RegEntry;
  113.  
  114. /*
  115.  *  DRAG&DROP SOURCE REGISTRATION RECORD
  116.  */
  117. typedef struct DD_SourceHndl {
  118.   char *dataType;               /* name of data type */
  119.   char *cmd;                    /* command used to send data */
  120.   struct DD_SourceHndl* next;   /* next handler in linked list */
  121. } DD_SourceHndl;
  122.  
  123. typedef struct {
  124.   DD_RegList *ddlist;           /* registration list containing this */
  125.  
  126.   Display *display;             /* drag&drop source window display */
  127.   Tk_Window tkwin;              /* drag&drop source window */
  128.   Atom ddAtom;                  /* X atom referring to "DragDropInfo" */
  129.   int button;                   /* button used to invoke drag for sources */
  130.  
  131.   Tk_Window tokenwin;           /* window representing drag item */
  132.   Tk_Anchor tokenAnchor;        /* position of token win relative to mouse */
  133.   Cursor tokenCursor;           /* cursor used when dragging token */
  134.   Tk_3DBorder tokenOutline;     /* outline around token window */
  135.   Tk_3DBorder tokenBorder;      /* border/background for token window */
  136.   int tokenBorderWidth;         /* border width in pixels */
  137.   XColor *rejectFg;             /* color used to draw rejection fg: (\) */
  138.   XColor *rejectBg;             /* color used to draw rejection bg: (\) */
  139.   Pixmap rejectSt;              /* stipple used to draw rejection: (\) */
  140.   GC rejectFgGC;                /* GC used to draw rejection fg: (\) */
  141.   GC rejectBgGC;                /* GC used to draw rejection bg: (\) */
  142.  
  143.   int pkgcmdInProg;             /* non-zero => executing pkgcmd */
  144.   char *pkgcmd;                 /* cmd executed on start of drag op */
  145.   char *pkgcmdResult;           /* result returned by recent pkgcmd */
  146.   char *sitecmd;                /* cmd executed to update token win */
  147.  
  148.   DD_WinRep *allwins;           /* window info (used during "drag") */
  149.   int selfTarget;               /* non-zero => source can drop onto itself */
  150.   int overTargetWin;            /* non-zero => over target window */
  151.   int tokenx, tokeny;           /* last position of token window */
  152.   Tk_TimerToken hidetoken;      /* token for routine to hide tokenwin */
  153.   Cursor normalCursor;        /* cursor restored after dragging */
  154.  
  155.   char *send;                   /* list of data handler names or "all" */
  156.   DD_SourceHndl *handlers;      /* list of data handlers */
  157. } DD_Source;
  158.  
  159. /*
  160.  *  STACK INFO
  161.  */
  162. typedef struct DD_Stack {
  163.   ClientData *values;        /* values on stack */
  164.   int len;            /* number of values on stack */
  165.   int max;            /* maximum size of stack */
  166.   ClientData space[5];        /* initial space for stack data */
  167. } DD_Stack;
  168.  
  169. /*
  170.  *  CONFIG PARAMETERS
  171.  */
  172. static Tk_ConfigSpec SourceConfigSpecs[] = {
  173.  
  174.   {TK_CONFIG_INT,
  175.      "-button", "buttonBinding", "ButtonBinding",
  176.      "3", Tk_Offset(DD_Source, button),
  177.      0},
  178.  
  179.   {TK_CONFIG_STRING,
  180.      "-packagecmd", "packageCommand", "Command",
  181.      NULL, Tk_Offset(DD_Source, pkgcmd),
  182.      TK_CONFIG_NULL_OK},
  183.  
  184.   {TK_CONFIG_COLOR,
  185.      "-rejectbg", "rejectBackground", "Background",
  186.      DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, rejectBg),
  187.      TK_CONFIG_COLOR_ONLY},
  188.   {TK_CONFIG_COLOR,
  189.      "-rejectbg", "rejectBackground", "Background",
  190.      "white", Tk_Offset(DD_Source, rejectBg),
  191.      TK_CONFIG_MONO_ONLY},
  192.  
  193.   {TK_CONFIG_COLOR,
  194.      "-rejectfg", "rejectForeground", "Foreground",
  195.      "red", Tk_Offset(DD_Source, rejectFg),
  196.      TK_CONFIG_COLOR_ONLY},
  197.   {TK_CONFIG_COLOR,
  198.      "-rejectfg", "rejectForeground", "Foreground",
  199.      "black", Tk_Offset(DD_Source, rejectFg),
  200.      TK_CONFIG_MONO_ONLY},
  201.  
  202.   {TK_CONFIG_BITMAP,
  203.      "-rejectstipple", "rejectStipple", "Stipple",
  204.      (char*)NULL, Tk_Offset(DD_Source, rejectSt),
  205.      TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
  206.   {TK_CONFIG_BITMAP,
  207.      "-rejectstipple", "rejectStipple", "Stipple",
  208.      "gray50", Tk_Offset(DD_Source, rejectSt),
  209.      TK_CONFIG_MONO_ONLY},
  210.  
  211.   {TK_CONFIG_BOOLEAN,
  212.      "-selftarget", "selfTarget", "SelfTarget",
  213.      "no", Tk_Offset(DD_Source, selfTarget),
  214.      0},
  215.  
  216.   {TK_CONFIG_STRING,
  217.      "-send", "send", "Send",
  218.      "all", Tk_Offset(DD_Source, send),
  219.      TK_CONFIG_NULL_OK},
  220.  
  221.   {TK_CONFIG_STRING,
  222.      "-sitecmd", "siteCommand", "Command",
  223.      NULL, Tk_Offset(DD_Source, sitecmd),
  224.      TK_CONFIG_NULL_OK},
  225.  
  226.   {TK_CONFIG_ANCHOR,
  227.      "-tokenanchor", "tokenAnchor", "Anchor",
  228.      "center", Tk_Offset(DD_Source, tokenAnchor),
  229.      0},
  230.  
  231.   {TK_CONFIG_BORDER,
  232.      "-tokenbg", "tokenBackground", "Background",
  233.      DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, tokenBorder),
  234.      TK_CONFIG_COLOR_ONLY},
  235.   {TK_CONFIG_BORDER,
  236.      "-tokenbg", "tokenBackground", "Background",
  237.      DEF_BUTTON_BG_MONO, Tk_Offset(DD_Source, tokenBorder),
  238.      TK_CONFIG_MONO_ONLY},
  239.  
  240.   {TK_CONFIG_BORDER,
  241.      "-tokenoutline", "tokenOutline", "Outline",
  242.      DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(DD_Source, tokenOutline),
  243.      TK_CONFIG_COLOR_ONLY},
  244.   {TK_CONFIG_BORDER,
  245.      "-tokenoutline", "tokenOutline", "Outline",
  246.      DEF_TOKEN_OUTLINE_MONO, Tk_Offset(DD_Source, tokenOutline),
  247.      TK_CONFIG_MONO_ONLY},
  248.  
  249.   {TK_CONFIG_PIXELS,
  250.      "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
  251.      "3", Tk_Offset(DD_Source, tokenBorderWidth),
  252.      0},
  253.  
  254.   {TK_CONFIG_CURSOR,
  255.      "-tokencursor", "tokenCursor", "Cursor",
  256.      "center_ptr", Tk_Offset(DD_Source, tokenCursor),
  257.      TK_CONFIG_NULL_OK},
  258.  
  259.   {TK_CONFIG_END,
  260.      (char*)NULL, (char*)NULL, (char*)NULL,
  261.      (char*)NULL, 0,
  262.      0},
  263. };
  264.  
  265.  
  266. /*
  267.  *  DRAG&DROP TARGET REGISTRATION RECORD
  268.  */
  269. typedef struct DD_TargetHndl {
  270.   char *dataType;               /* Name of data type (malloc-ed) */
  271.   char *command;                /* command to handle data type (malloc-ed) */
  272.   struct DD_TargetHndl* next;   /* next handler in linked list */
  273. } DD_TargetHndl;
  274.  
  275. typedef struct {
  276.   DD_RegList *ddlist;           /* registration list containing this */
  277.  
  278.   Display *display;             /* drag&drop target window display */
  279.   Tk_Window tkwin;              /* drag&drop target window */
  280.   DD_TargetHndl *handlers;      /* list of data handlers */
  281. } DD_Target;
  282.  
  283. /*
  284.  * Each "drag&drop" widget window is tagged with a "DragDropInfo"
  285.  * property in XA_STRING format.  This property identifies the
  286.  * window as a "drag&drop" widget, and contains the following:
  287.  *
  288.  *     "<interp-name>]<drag&drop-path>]<handler-list>"
  289.  *
  290.  * The <drag&drop-path> is the window path name of the drag&drop
  291.  * widget, <interp-name> is the name of the interpreter controlling
  292.  * the widget (useful for the "send" command), and <handler-list>
  293.  * is the list of handler types recognized by the widget.
  294.  *
  295.  * When the user invokes the "drag" operation, a snapshot of the
  296.  * entire window hierarchy is made, and windows carrying a
  297.  * "DragDropInfo" property are identified.  As the token window is
  298.  * dragged around, * this snapshot can be queried to determine when
  299.  * the token is over a valid target window.  When the token is
  300.  * dropped over a valid site, the drop information is sent to the
  301.  * application via the usual "send" command.  If communication fails,
  302.  * the drag&drop facility automatically posts a rejection symbol on
  303.  * the token window.
  304.  */
  305.  
  306. /*
  307.  *  Maximum size property that can be read at one time:
  308.  */
  309. #define MAX_PROP_SIZE 1000
  310.  
  311. /*
  312.  *  FORWARD DECLARATIONS
  313.  */
  314. int Blt_DragDropInit _ANSI_ARGS_((Tcl_Interp* interp));
  315.  
  316. static void DragDrop_Delete _ANSI_ARGS_((ClientData clientData));
  317. static int DragDrop_Cmd _ANSI_ARGS_((ClientData clientData,
  318.                      Tcl_Interp *interp, int argc, char **argv));
  319.  
  320. static DD_Source* GetSourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  321.                          char *pathname, int *newEntry));
  322. static void DestroySourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  323.                        char *pathName));
  324. static int ConfigSource _ANSI_ARGS_((Tcl_Interp *interp, DD_Source *dsPtr,
  325.                      int argc, char **argv, int flags));
  326. static char* FindSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname));
  327. static void PutSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname,
  328.                       char *cmd));
  329. static DD_SourceHndl* CreateSourceHandler _ANSI_ARGS_((char *dtname,
  330.                                char *cmd));
  331. static void DestroySourceHandler _ANSI_ARGS_((DD_SourceHndl *dsHndl));
  332. static void UnregSource _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  333.  
  334. static DD_Target* GetTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  335.                          char *pathname, int *newEntry));
  336. static void DestroyTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  337.                        char *pathName));
  338. static char* FindTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname));
  339. static void PutTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname,
  340.                       char *cmd));
  341. static DD_TargetHndl* CreateTargetHandler _ANSI_ARGS_((char *dtname,
  342.                                char *cmd));
  343. static void DestroyTargetHandler _ANSI_ARGS_((DD_TargetHndl *dtHndl));
  344. static void UnregTarget _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  345.  
  346. static void DragDropSend _ANSI_ARGS_((DD_Source *dsPtr));
  347. static char* DragDropSendHndlr _ANSI_ARGS_((DD_Source *dsPtr,
  348.                         char *interpName, char *ddName));
  349.  
  350. static DD_WinRep* GetWinRepInfo _ANSI_ARGS_((DD_Source *dsPtr,
  351.                          DD_RegList *ddlist));
  352. static DD_WinRep* FindTargetWin _ANSI_ARGS_((DD_Source *dsPtr, int x, int y));
  353. static DD_WinRep* WinRepAlloc _ANSI_ARGS_((DD_RegList* ddlist));
  354. static void WinRepRelease _ANSI_ARGS_((DD_WinRep* wr, DD_RegList* ddlist));
  355. static void WinRepInit _ANSI_ARGS_((DD_WinRep* wr, DD_Source *dsPtr));
  356.  
  357. static void AddDDProp _ANSI_ARGS_((DD_Target *dtPtr));
  358. static void DDTokenEventProc _ANSI_ARGS_((ClientData, XEvent*));
  359. static void MoveDDToken _ANSI_ARGS_((DD_Source *dsPtr));
  360. static void UpdateDDToken _ANSI_ARGS_((ClientData clientData));
  361. static void HideDDToken _ANSI_ARGS_((ClientData clientData));
  362. static void RejectDDToken _ANSI_ARGS_((DD_Source* dsPtr));
  363.  
  364. static void StackInit _ANSI_ARGS_((DD_Stack *stack));
  365. static void StackDelete _ANSI_ARGS_((DD_Stack *stack));
  366. static void StackPush _ANSI_ARGS_((ClientData cdata, DD_Stack *stack));
  367. static ClientData StackPop _ANSI_ARGS_((DD_Stack *stack));
  368.  
  369.  
  370. /*
  371.  * ------------------------------------------------------------------------
  372.  *  Blt_DragDropInit()
  373.  *
  374.  *  Adds the drag&drop command to the given interpreter.  Should be
  375.  *  invoked to properly install the command whenever a new interpreter
  376.  *  is created.
  377.  * ------------------------------------------------------------------------
  378.  */
  379. int
  380. Blt_DragDropInit(interp)
  381.      Tcl_Interp *interp;  /* interpreter to be updated */
  382. {
  383.   DD_RegList *ddlist;
  384.   Tk_Window tkwin;
  385.   Tcl_CmdInfo cmdInfo;
  386.  
  387.   /*
  388.    *  See if drag&drop is already installed.
  389.    */
  390.   if (Tcl_GetCommandInfo(interp, DRAGDROP_COMMAND, &cmdInfo))
  391.     {
  392.       Tcl_ResetResult(interp);
  393.       Tcl_AppendResult(interp, "already installed: ", DRAGDROP_COMMAND,
  394.                (char*)NULL);
  395.       return TCL_ERROR;
  396.     }
  397.  
  398.   /*
  399.    *  Make sure that this is a Tk application.
  400.    */
  401.   tkwin = Tk_MainWindow(interp);
  402.   if (tkwin == None)
  403.     {
  404.       Tcl_ResetResult(interp);
  405.       Tcl_AppendResult(interp, "requires Tk facilities: ", DRAGDROP_COMMAND,
  406.                (char*)NULL);
  407.       return TCL_ERROR;
  408.     }
  409.  
  410.   /*
  411.    *  Install drag&drop facilities.
  412.    */
  413.   ddlist = (DD_RegList*)malloc(sizeof(DD_RegList));
  414.   ddlist->interp = interp;
  415.   ddlist->root = tkwin;
  416.   Tcl_InitHashTable(&ddlist->srcList,TCL_STRING_KEYS);
  417.   Tcl_InitHashTable(&ddlist->trgList,TCL_STRING_KEYS);
  418.   ddlist->errorProc = strdup(DRAGDROP_ERRORPROC);
  419.   ddlist->numactive = 0;
  420.   ddlist->locx = ddlist->locy = 0;
  421.   ddlist->pool = NULL;
  422.  
  423.   Tcl_CreateCommand(interp, DRAGDROP_COMMAND, DragDrop_Cmd,
  424.             (ClientData)ddlist, DragDrop_Delete);
  425.  
  426.   Tcl_SetVar2(interp, "blt_versions", DRAGDROP_COMMAND, DRAGDROP_VERSION,
  427.           TCL_GLOBAL_ONLY);
  428.  
  429.   return TCL_OK;
  430. }
  431.  
  432. /*
  433.  * ------------------------------------------------------------------------
  434.  *  DragDrop_Delete()
  435.  *
  436.  *  Invoked when the drag&drop command is removed from an interpreter
  437.  *  to free up allocated memory.
  438.  * ------------------------------------------------------------------------
  439.  */
  440. static void
  441. DragDrop_Delete(cdata)
  442.      ClientData cdata;    /* client data for drag&drop command */
  443. {
  444.   DD_RegList *ddlist = (DD_RegList*)cdata;
  445.   DD_WinRep *wrpool, *wrnext;
  446.  
  447.   Tcl_DeleteHashTable(&ddlist->srcList);
  448.   Tcl_DeleteHashTable(&ddlist->trgList);
  449.   if (ddlist->errorProc != NULL) {
  450.     free((char*)ddlist->errorProc);
  451.   }
  452.   for (wrpool=ddlist->pool; wrpool; wrpool=wrnext)
  453.     {
  454.       wrnext = wrpool->next;
  455.       free((char*)wrpool);
  456.     }
  457.   free((char*)ddlist);
  458. }
  459.  
  460. /*
  461.  * ------------------------------------------------------------------------
  462.  *  DragDrop_Cmd()
  463.  *
  464.  *  Invoked by TCL whenever the user issues a drag&drop command.
  465.  *  Handles the following syntax:
  466.  *
  467.  *    blt_drag&drop source
  468.  *    blt_drag&drop source <pathName>
  469.  *    blt_drag&drop source <pathName> config ?options...?
  470.  *    blt_drag&drop source <pathName> handler <dataType> <command> ?...?
  471.  *
  472.  *    blt_drag&drop target
  473.  *    blt_drag&drop target <pathName> handler
  474.  *    blt_drag&drop target <pathName> handler <dataType> <command> ?...?
  475.  *    blt_drag&drop target <pathName> handle <dataType>
  476.  *
  477.  *    blt_drag&drop drag <pathName> <x> <y>
  478.  *    blt_drag&drop drop <pathName> <x> <y>
  479.  *
  480.  *    blt_drag&drop errors ?<proc>?
  481.  *    blt_drag&drop active
  482.  *    blt_drag&drop location ?<x> <y>?
  483.  *
  484.  * ------------------------------------------------------------------------
  485.  */
  486. static int
  487. DragDrop_Cmd(clientData, interp, argc, argv)
  488.      ClientData clientData;   /* main window associated with interp */
  489.      Tcl_Interp *interp;      /* current interpreter */
  490.      int argc;                /* number of arguments */
  491.      char **argv;             /* argument strings */
  492. {
  493.   DD_RegList *ddlist = (DD_RegList*)clientData;
  494.   DD_RegEntry *ddentry;
  495.   register DD_Source *dsPtr;
  496.   register DD_Target *dtPtr;
  497.  
  498.   int status, length, x, y, newEntry;
  499.   char c;
  500.  
  501.   Tk_Window tokenwin;
  502.   XSetWindowAttributes atts;
  503.   char buffer[1024];
  504.  
  505.   if (argc < 2)
  506.     {
  507.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  508.                argv[0], " option ?args?\"",
  509.                (char*)NULL);
  510.       return TCL_ERROR;
  511.     }
  512.   c = argv[1][0];
  513.   length = strlen(argv[1]);
  514.  
  515.   /*
  516.    *  HANDLE:  blt_drag&drop source
  517.    *           blt_drag&drop source <pathName>
  518.    *           blt_drag&drop source <pathName> config ?options...?
  519.    *           blt_drag&drop source <pathName> handler <data> <scmd> ?...?
  520.    */
  521.   if ((c == 's') && strncmp(argv[1], "source", length) == 0)
  522.     {
  523.       /*
  524.        *  HANDLE:  blt_drag&drop source
  525.        */
  526.       if (argc == 2)
  527.     {
  528.       Tcl_HashSearch cursor;
  529.       Tcl_HashEntry *entryPtr;
  530.       char *name;
  531.             
  532.       for (entryPtr = Tcl_FirstHashEntry(&ddlist->srcList, &cursor);
  533.            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) 
  534.         {
  535.           name = Tcl_GetHashKey(&ddlist->srcList, entryPtr);
  536.           Tcl_AppendElement(interp, name);
  537.         }
  538.       return TCL_OK;
  539.     }
  540.  
  541.       /*
  542.        *  Find or create source info...
  543.        */
  544.       dsPtr = GetSourceInfo(ddlist, argv[2], &newEntry);
  545.       dsPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  546.       if (!dsPtr->tkwin)
  547.     {
  548.       Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  549.                (char*)NULL);
  550.       DestroySourceInfo(ddlist,argv[2]);
  551.       return TCL_ERROR;
  552.     }
  553.       dsPtr->display = Tk_Display(dsPtr->tkwin);
  554.       dsPtr->ddAtom  = XInternAtom(dsPtr->display, DRAGDROP_PROPINFO, False);
  555.  
  556.       if (newEntry)
  557.     ConfigSource(interp, dsPtr, 0, (char**)NULL, 0);
  558.  
  559.       /*
  560.        *  HANDLE:  blt_drag&drop source <pathName> config ?options...?
  561.        */
  562.       if (argc > 3)
  563.     {
  564.       c = argv[3][0];
  565.       length = strlen(argv[3]);
  566.  
  567.       if ((c == 'c') && strncmp(argv[3], "config", length) == 0)
  568.         {
  569.           if (argc == 4)
  570.         status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  571.                       SourceConfigSpecs, (char*)dsPtr, (char*)NULL, 0);
  572.  
  573.           else if (argc == 5)
  574.         status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  575.                       SourceConfigSpecs, (char*)dsPtr, argv[4], 0);
  576.  
  577.           else
  578.         status = ConfigSource(interp, dsPtr, argc-4, argv+4,
  579.                       TK_CONFIG_ARGV_ONLY);
  580.         }
  581.  
  582.       /*
  583.        *  HANDLE:  blt_drag&drop source <pathName> handler \
  584.        *             ?<data> <scmd>...?
  585.        */
  586.       else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0)
  587.         {
  588.           if (argc == 4)
  589.         {
  590.           DD_SourceHndl *dsHndl = dsPtr->handlers;
  591.           while (dsHndl)
  592.             {
  593.               Tcl_AppendElement(interp, dsHndl->dataType);
  594.               dsHndl = dsHndl->next;
  595.             }
  596.           return TCL_OK;
  597.         }
  598.  
  599.           /*
  600.            *  Process handler definitions
  601.            */
  602.           status = TCL_OK;
  603.           for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  604.         {
  605.           if (x+1 < argc)
  606.             {
  607.               char *p;
  608.               for (p=argv[x]; *p != '\0'; p++)
  609.             if (*p == ' ')
  610.               {
  611.                 Tcl_AppendResult(interp,
  612.                          "bad source handler name \"",
  613.                          argv[x], "\" (should not contain spaces)",
  614.                          (char*)NULL);
  615.                 return TCL_ERROR;
  616.               }
  617.  
  618.               PutSourceHandler(dsPtr, argv[x], argv[x+1]);
  619.             }
  620.           else
  621.             {
  622.               Tcl_AppendResult(interp,
  623.                        "missing command for source handler: should be \"",
  624.                        argv[x], " command\"");
  625.               return TCL_ERROR;
  626.             }
  627.         }
  628.           return TCL_OK;
  629.         }
  630.       else
  631.         {
  632.           Tcl_AppendResult(interp, "bad option \"", argv[3],
  633.                    "\": must be config or handler",
  634.                    (char*)NULL);
  635.           return TCL_ERROR;
  636.         }
  637.     }
  638.  
  639.       if (newEntry)
  640.     {
  641.       /*
  642.        *  Create the window for the drag&drop token...
  643.        */
  644.       sprintf(buffer, "dd-token%x", (int)dsPtr);
  645.       tokenwin = Tk_CreateWindow(dsPtr->ddlist->interp, dsPtr->tkwin,
  646.                      buffer, "");
  647.  
  648.       if (!tokenwin)
  649.         {
  650.           Tcl_AppendResult(interp, "could not create token window",
  651.                    (char*)NULL);
  652.           DestroySourceInfo(ddlist,argv[2]);
  653.           return TCL_ERROR;
  654.         }
  655.       Tk_SetClass(tokenwin, DRAGDROP_CLASS);
  656.       Tk_CreateEventHandler(tokenwin, ExposureMask|StructureNotifyMask,
  657.                 DDTokenEventProc, (ClientData)dsPtr);
  658.  
  659.       atts.override_redirect = True;
  660.       atts.save_under = True;
  661.       Tk_ChangeWindowAttributes(tokenwin,
  662.                     CWOverrideRedirect|CWSaveUnder, &atts);
  663.  
  664.       Tk_SetInternalBorder(tokenwin, 2*dsPtr->tokenBorderWidth);
  665.       dsPtr->tokenwin = tokenwin;
  666.  
  667.       if (dsPtr->button > 0)
  668.         {
  669.           sprintf(buffer,
  670.               "bind %.100s <ButtonPress-%d> {%s drag %.100s %%X %%Y}; \
  671.                     bind %.100s <B%d-Motion> {%s drag %.100s %%X %%Y}; \
  672.                     bind %.100s <ButtonRelease-%d> {%s drop %.100s %%X %%Y}",
  673.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  674.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  675.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2]);
  676.  
  677.           if (Tcl_Eval(interp, buffer) != TCL_OK)
  678.         {
  679.           Tk_DestroyWindow(tokenwin);
  680.           DestroySourceInfo(ddlist,argv[2]);
  681.           return TCL_ERROR;
  682.         }
  683.         }
  684.  
  685.       /*
  686.        *  Arrange for the window to unregister itself when it
  687.        *  is destroyed.
  688.        */
  689.       ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  690.       ddentry->ddlist = ddlist;
  691.       ddentry->tkwin = dsPtr->tkwin;
  692.       Tk_CreateEventHandler(dsPtr->tkwin, StructureNotifyMask,
  693.                 UnregSource, (ClientData)ddentry);
  694.     }
  695.     }
  696.  
  697.   /*
  698.    *  HANDLE:  blt_drag&drop target ?<pathName>? ?handling info...?
  699.    */
  700.   else if ((c == 't') && strncmp(argv[1], "target", length) == 0)
  701.     {
  702.       /*
  703.        *  HANDLE:  blt_drag&drop target
  704.        */
  705.       if (argc == 2)
  706.     {
  707.       Tcl_HashSearch pos;
  708.       Tcl_HashEntry *entry = Tcl_FirstHashEntry(&ddlist->trgList,&pos);
  709.       while (entry)
  710.         {
  711.           Tcl_AppendElement(interp,
  712.                 Tcl_GetHashKey(&ddlist->trgList,entry));
  713.           entry = Tcl_NextHashEntry(&pos);
  714.         }
  715.       return TCL_OK;
  716.     }
  717.  
  718.       dtPtr = GetTargetInfo(ddlist,argv[2],&newEntry);
  719.       dtPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  720.  
  721.       if (!dtPtr->tkwin)
  722.     {
  723.       Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  724.                (char*)NULL);
  725.       DestroyTargetInfo(ddlist,argv[2]);
  726.       return TCL_ERROR;
  727.     }
  728.       dtPtr->display = Tk_Display(dtPtr->tkwin);
  729.  
  730.       /*
  731.        *  If this is a new target, attach a property to identify
  732.        *  window as "drag&drop" target, and arrange for the window
  733.        *  to un-register itself when it is destroyed.
  734.        */
  735.       if (newEntry)
  736.     {
  737.       Tk_MakeWindowExist(dtPtr->tkwin);
  738.       AddDDProp(dtPtr);
  739.  
  740.       /*
  741.        *  Arrange for the window to unregister itself when it
  742.        *  is destroyed.
  743.        */
  744.       ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  745.       ddentry->ddlist = ddlist;
  746.       ddentry->tkwin = dtPtr->tkwin;
  747.       Tk_CreateEventHandler(dtPtr->tkwin, StructureNotifyMask,
  748.                 UnregTarget, (ClientData)ddentry);
  749.     }
  750.  
  751.       /*
  752.        *  HANDLE:  blt_drag&drop target <pathName> handler
  753.        *           blt_drag&drop target <pathName> handler <data> <cmd> ?...?
  754.        */
  755.       if ((argc >= 4) && (strcmp(argv[3], "handler") == 0))
  756.     {
  757.       if (argc == 4)
  758.         {
  759.           DD_TargetHndl *dtHndl = dtPtr->handlers;
  760.           while (dtHndl)
  761.         {
  762.           Tcl_AppendElement(interp, dtHndl->dataType);
  763.           dtHndl = dtHndl->next;
  764.         }
  765.           return TCL_OK;
  766.         }
  767.  
  768.       /*
  769.        *  Process handler definitions
  770.        */
  771.       status = TCL_OK;
  772.       for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  773.         {
  774.           if (x+1 < argc)
  775.         PutTargetHandler(dtPtr, argv[x], argv[x+1]);
  776.           else
  777.         {
  778.           Tcl_AppendResult(interp,
  779.                    "missing command for target handler: should be \"",
  780.                    argv[x], " command\"",
  781.                    (char*)NULL);
  782.           return TCL_ERROR;
  783.         }
  784.         }
  785.       return TCL_OK;
  786.     }
  787.  
  788.       /*
  789.        *  HANDLE:  blt_drag&drop target <pathName> handle <data>
  790.        */
  791.       else if ((argc == 5) && (strcmp(argv[3], "handle") == 0))
  792.     {
  793.       char *cmd;
  794.       if ((cmd=FindTargetHandler(dtPtr, argv[4])) != NULL)
  795.         return Tcl_Eval(interp, cmd);
  796.  
  797.       Tcl_AppendResult(interp, "target cannot handle datatype: ",
  798.                argv[4], (char*)NULL);
  799.       return TCL_ERROR;    /* no handler found */
  800.     }
  801.       else
  802.     {
  803.       Tcl_AppendResult(interp,"usage: ", argv[0], " target ", argv[2],
  804.                " handler ?defns?\n   or: ", argv[0], " target ", argv[2],
  805.                " handle <data>",
  806.                (char*)NULL);
  807.       return TCL_ERROR;
  808.     }
  809.     }
  810.  
  811.   /*
  812.    *  HANDLE:  blt_drag&drop drag <path> <x> <y>
  813.    */
  814.   else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0)
  815.     {
  816.       if (argc < 5)
  817.     {
  818.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  819.                argv[0], " drag pathname x y\"",
  820.                (char*)NULL);
  821.       return TCL_ERROR;
  822.     }
  823.  
  824.       dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  825.       if (newEntry)
  826.     {
  827.       Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  828.                (char*)NULL);
  829.       DestroySourceInfo(ddlist,argv[2]);
  830.       return TCL_ERROR;
  831.     }
  832.       if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  833.       (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  834.     return TCL_ERROR;
  835.  
  836.       ddlist->locx = x;        /* save drag&drop location */
  837.       ddlist->locy = y;
  838.       dsPtr->tokenx = x;
  839.       dsPtr->tokeny = y;
  840.  
  841.       /*
  842.        *  If HideDDToken() is pending, then do it now!
  843.        */
  844.       if (dsPtr->hidetoken)
  845.     {
  846.       Tk_DeleteTimerHandler(dsPtr->hidetoken);
  847.       HideDDToken((ClientData)dsPtr);
  848.     }
  849.  
  850.       /*
  851.        *  If pkgcmd is in progress, then ignore subsequent calls
  852.        *  until it completes.  Only perform drag if pkgcmd
  853.        *  completed successfully and token window is mapped.
  854.        */
  855.       if (!Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  856.     {
  857.       /*
  858.        *  No list of send handlers?  Then source is disabled.
  859.        *  Abort drag quietly.
  860.        */
  861.       if (dsPtr->send == NULL)
  862.         return TCL_OK;
  863.  
  864.       /*
  865.        *  No token command?  Then cannot build token.
  866.        *  Signal error.
  867.        */
  868.       if (!dsPtr->pkgcmd)
  869.         {
  870.           Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
  871.                    (char*)NULL);
  872.           return TCL_ERROR;
  873.         }
  874.  
  875.       /*
  876.        *  Execute token command to initialize token window.
  877.        */
  878.       dsPtr->pkgcmdInProg = ~0;
  879.       status = Tcl_VarEval(dsPtr->ddlist->interp,
  880.                    dsPtr->pkgcmd, " ", Tk_PathName(dsPtr->tokenwin),
  881.                    (char*)NULL);
  882.       dsPtr->pkgcmdInProg = 0;
  883.  
  884.       /*
  885.        *  Null string from the package command?
  886.        *  Then quietly abort the drag&drop operation.
  887.        */
  888.       if (*interp->result == '\0')
  889.         return TCL_OK;
  890.  
  891.       /*
  892.        *  Save result of token command for send command.
  893.        */
  894.       if (dsPtr->pkgcmdResult)
  895.         free(dsPtr->pkgcmdResult);
  896.       dsPtr->pkgcmdResult = strdup(interp->result);
  897.  
  898.       /*
  899.        *  Token building failed?  If an error handler is defined,
  900.        *  then signal the error.  Otherwise, abort quietly.
  901.        */
  902.       if (status != TCL_OK)
  903.         {
  904.           if (ddlist->errorProc && *ddlist->errorProc)
  905.         {
  906.           return Tcl_VarEval(ddlist->interp,
  907.                      ddlist->errorProc, " {", ddlist->interp->result, "}",
  908.                      (char*)NULL);
  909.         }
  910.           else
  911.         return TCL_OK;
  912.         }
  913.  
  914.       /*
  915.        *  Install token cursor...
  916.        */
  917.       if (dsPtr->tokenCursor != None)
  918.         {
  919.           status = Tcl_VarEval(dsPtr->ddlist->interp,
  920.                    Tk_PathName(dsPtr->tkwin), " config -cursor",
  921.                    (char*)NULL);
  922.  
  923.           if (status == TCL_OK)
  924.         {
  925.           char *cname = interp->result;
  926.           while (*cname != '\0')
  927.             cname++;
  928.  
  929.           while ((cname > interp->result) && (*(cname-1) != ' '))
  930.             cname--;
  931.  
  932.           if (dsPtr->normalCursor != None)
  933.             {
  934.               Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  935.               dsPtr->normalCursor = None;
  936.             }
  937.  
  938.           if (strcmp(cname,"{}") != 0)
  939.             dsPtr->normalCursor = Tk_GetCursor(interp,
  940.                                dsPtr->tkwin, Tk_GetUid(cname));
  941.         }
  942.           Tk_DefineCursor(dsPtr->tkwin, dsPtr->tokenCursor);
  943.         }
  944.  
  945.       /*
  946.        *  Get ready to drag token window...
  947.        *  1) Cache info for all windows on root
  948.        *  2) Map token window to begin drag operation
  949.        */
  950.       if (dsPtr->allwins)
  951.         WinRepRelease(dsPtr->allwins, ddlist);
  952.       dsPtr->allwins = GetWinRepInfo(dsPtr, ddlist);
  953.  
  954.       ddlist->numactive++;    /* one more drag&drop window active */
  955.       Tk_MapWindow(dsPtr->tokenwin);
  956.       XRaiseWindow(Tk_Display(dsPtr->tokenwin),
  957.                Tk_WindowId(dsPtr->tokenwin));
  958.     }
  959.  
  960.       /*
  961.        *  Arrange to update status of token window...
  962.        */
  963.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  964.       Tk_DoWhenIdle(UpdateDDToken, (ClientData)dsPtr);
  965.  
  966.       /*
  967.        *  Move the token window to the current drag point...
  968.        */
  969.       MoveDDToken(dsPtr);
  970.     }
  971.  
  972.   /*
  973.    *  HANDLE:  blt_drag&drop drop <path> <x> <y>
  974.    */
  975.   else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0)
  976.     {
  977.       if (argc < 5)
  978.     {
  979.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  980.                argv[0], " drop pathname x y\"",
  981.                (char*)NULL);
  982.       return TCL_ERROR;
  983.     }
  984.  
  985.       dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  986.       if (newEntry)
  987.     {
  988.       Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  989.                (char*)NULL);
  990.       DestroySourceInfo(ddlist,argv[2]);
  991.       return TCL_ERROR;
  992.     }
  993.       if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  994.       (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  995.     return TCL_ERROR;
  996.  
  997.       ddlist->locx = x;        /* save drag&drop location */
  998.       ddlist->locy = y;
  999.       dsPtr->tokenx = x;
  1000.       dsPtr->tokeny = y;
  1001.  
  1002.       /*
  1003.        *  Put the cursor back to its usual state.
  1004.        */
  1005.       if (dsPtr->normalCursor == None)
  1006.     Tk_UndefineCursor(dsPtr->tkwin);
  1007.       else
  1008.     Tk_DefineCursor(dsPtr->tkwin, dsPtr->normalCursor);
  1009.  
  1010.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1011.  
  1012.       /*
  1013.        *  Make sure that token window was not dropped before it
  1014.        *  was either mapped or packed with info.
  1015.        */
  1016.       if (Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  1017.     {
  1018.       UpdateDDToken((ClientData)dsPtr);
  1019.  
  1020.       if (dsPtr->send)
  1021.         {
  1022.           if (dsPtr->overTargetWin)
  1023.         DragDropSend(dsPtr);
  1024.           else
  1025.         HideDDToken((ClientData)dsPtr);
  1026.         }
  1027.       ddlist->numactive--;  /* one fewer active token window */
  1028.     }
  1029.     }
  1030.  
  1031.   /*
  1032.    *  HANDLE:  blt_drag&drop errors ?<proc>?
  1033.    */
  1034.   else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0)
  1035.     {
  1036.       if (argc == 3)
  1037.     {
  1038.       if (ddlist->errorProc) {
  1039.         free(ddlist->errorProc);
  1040.       }
  1041.       ddlist->errorProc = strdup(argv[2]);
  1042.     }
  1043.       else if (argc != 2)
  1044.     {
  1045.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1046.                argv[0], " errors ?proc?\"",
  1047.                (char*)NULL);
  1048.       return TCL_ERROR;
  1049.     }
  1050.       Tcl_SetResult(interp, ddlist->errorProc, TCL_VOLATILE);
  1051.       return TCL_OK;
  1052.     }
  1053.  
  1054.   /*
  1055.    *  HANDLE:  blt_drag&drop active
  1056.    */
  1057.   else if ((c == 'a') && strncmp(argv[1], "active", length) == 0)
  1058.     {
  1059.       if (argc != 2)
  1060.     {
  1061.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1062.                argv[0], " active\"",
  1063.                (char*)NULL);
  1064.       return TCL_ERROR;
  1065.     }
  1066.       Tcl_SetResult(interp, (ddlist->numactive > 0) ? "1" : "0", TCL_STATIC);
  1067.       return TCL_OK;
  1068.     }
  1069.  
  1070.   /*
  1071.    *  HANDLE:  blt_drag&drop location ?<x> <y>?
  1072.    */
  1073.   else if ((c == 'l') && strncmp(argv[1], "location", length) == 0)
  1074.     {
  1075.       if (argc == 2)
  1076.     {
  1077.       sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1078.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1079.       return TCL_OK;
  1080.     }
  1081.       else if ((argc == 4) &&
  1082.            (Tcl_GetInt(interp, argv[2], &x) == TCL_OK) &&
  1083.            (Tcl_GetInt(interp, argv[3], &y) == TCL_OK))
  1084.     {
  1085.       ddlist->locx = x;
  1086.       ddlist->locy = y;
  1087.       sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1088.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1089.       return TCL_OK;
  1090.     }
  1091.       else
  1092.     {
  1093.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1094.                argv[0], " location ?x y?\"",
  1095.                (char*)NULL);
  1096.       return TCL_ERROR;
  1097.     }
  1098.     }
  1099.  
  1100.   /*
  1101.    *  Report improper command arguments
  1102.    */
  1103.   else
  1104.     {
  1105.       Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be source, target, drag, drop, errors, active or location",
  1106.                (char*)NULL);
  1107.       return TCL_ERROR;
  1108.     }
  1109.   return TCL_OK;
  1110. }
  1111.  
  1112. /*
  1113.  * ------------------------------------------------------------------------
  1114.  *  GetSourceInfo()
  1115.  *
  1116.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1117.  *  widgets.  Creates a new record if the widget name is not already
  1118.  *  registered.  Returns a pointer to the desired record.
  1119.  * ------------------------------------------------------------------------
  1120.  */
  1121. static DD_Source*
  1122. GetSourceInfo(ddlist,pathname,newEntry)
  1123.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1124.      char* pathname;      /* widget pathname for desired record */
  1125.      int* newEntry;       /* returns non-zero => new record created */
  1126. {
  1127.   DD_Source *dsPtr;
  1128.   Tcl_HashEntry *ddEntry;
  1129.  
  1130.   ddEntry = Tcl_CreateHashEntry(&ddlist->srcList, pathname, newEntry);
  1131.   if (*newEntry)
  1132.     {
  1133.       /*
  1134.        *  Initialize a data structure for the widget...
  1135.        */
  1136.       dsPtr = (DD_Source*)malloc(sizeof(DD_Source));
  1137.       dsPtr->ddlist = ddlist;
  1138.       dsPtr->display = NULL;
  1139.       dsPtr->tkwin = NULL;
  1140.       dsPtr->ddAtom = None;
  1141.       dsPtr->button = 0;
  1142.  
  1143.       dsPtr->tokenwin = NULL;
  1144.       dsPtr->tokenAnchor = TK_ANCHOR_CENTER;
  1145.       dsPtr->tokenCursor = None;
  1146.       dsPtr->tokenOutline = NULL;
  1147.       dsPtr->tokenBorder = NULL;
  1148.       dsPtr->tokenBorderWidth = 0;
  1149.       dsPtr->rejectFg = NULL;
  1150.       dsPtr->rejectBg = NULL;
  1151.       dsPtr->rejectSt = None;
  1152.       dsPtr->rejectFgGC = None;
  1153.       dsPtr->rejectBgGC = None;
  1154.  
  1155.       dsPtr->pkgcmdInProg = 0;
  1156.       dsPtr->pkgcmd = NULL;
  1157.       dsPtr->pkgcmdResult = NULL;
  1158.       dsPtr->sitecmd = NULL;
  1159.  
  1160.       dsPtr->allwins = NULL;
  1161.       dsPtr->selfTarget = 0;
  1162.       dsPtr->overTargetWin = 0;
  1163.       dsPtr->tokenx = dsPtr->tokeny = 0;
  1164.       dsPtr->hidetoken = NULL;
  1165.       dsPtr->normalCursor = None;
  1166.  
  1167.       dsPtr->send = NULL;
  1168.       dsPtr->handlers = NULL;
  1169.  
  1170.       Tcl_SetHashValue(ddEntry, (ClientData)dsPtr);
  1171.     }
  1172.   return (DD_Source*)Tcl_GetHashValue(ddEntry);
  1173. }
  1174.  
  1175. /*
  1176.  * ------------------------------------------------------------------------
  1177.  *  DestroySourceInfo()
  1178.  *
  1179.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1180.  *  widgets.  Destroys the record if found.
  1181.  * ------------------------------------------------------------------------
  1182.  */
  1183. static void
  1184. DestroySourceInfo(ddlist,pathname)
  1185.      DD_RegList* ddlist;     /* drag&drop records for all registered widgets */
  1186.      char* pathname;         /* widget pathname for desired record */
  1187. {
  1188.   DD_Source *dsPtr;
  1189.   DD_SourceHndl *dsHndl, *next;
  1190.   Tcl_HashEntry *ddEntry;
  1191.  
  1192.   ddEntry = Tcl_FindHashEntry(&ddlist->srcList, pathname);
  1193.   if (ddEntry == NULL) {
  1194.     return;
  1195.   }
  1196.   dsPtr = (DD_Source*)Tcl_GetHashValue(ddEntry);
  1197.   if (dsPtr)
  1198.     {
  1199.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1200.       if (dsPtr->hidetoken)           
  1201.     Tk_DeleteTimerHandler(dsPtr->hidetoken);
  1202.  
  1203.       Tk_FreeOptions(SourceConfigSpecs, (char *)dsPtr, dsPtr->display, 0);
  1204.  
  1205.       if (dsPtr->rejectFgGC != None) 
  1206.     Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1207.       if (dsPtr->rejectBgGC != None) 
  1208.     Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1209.       if (dsPtr->pkgcmdResult)       
  1210.     free(dsPtr->pkgcmdResult);
  1211.  
  1212.       if (dsPtr->allwins)               
  1213.     WinRepRelease(dsPtr->allwins, ddlist);
  1214.  
  1215.       if (dsPtr->normalCursor != None)
  1216.     Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  1217.  
  1218.       dsHndl = dsPtr->handlers;
  1219.       while (dsHndl)
  1220.     {
  1221.       next = dsHndl->next;
  1222.       DestroySourceHandler(dsHndl);
  1223.       dsHndl = next;
  1224.     }
  1225.       free((char*)dsPtr);
  1226.     }
  1227.   Tcl_DeleteHashEntry(ddEntry);
  1228. }
  1229.  
  1230. /*
  1231.  * ------------------------------------------------------------------------
  1232.  *  ConfigSource()
  1233.  *
  1234.  *  Called to process an (argc,argv) list to configure (or reconfigure)
  1235.  *  a drag&drop source widget.
  1236.  * ------------------------------------------------------------------------
  1237.  */
  1238. static int
  1239. ConfigSource(interp, dsPtr, argc, argv, flags)
  1240.      Tcl_Interp *interp;        /* current interpreter */
  1241.      register DD_Source *dsPtr; /* drag&drop source widget record */
  1242.      int argc;                  /* number of arguments */
  1243.      char **argv;               /* argument strings */
  1244.      int flags;                 /* flags controlling interpretation */
  1245. {
  1246.   unsigned long gcMask;
  1247.   XGCValues gcValues;
  1248.   GC newGC;
  1249.  
  1250.   /*
  1251.    *  Handle the bulk of the options...
  1252.    */
  1253.   if (Tk_ConfigureWidget(interp, dsPtr->tkwin, SourceConfigSpecs,
  1254.              argc, argv, (char*)dsPtr, flags) != TCL_OK)
  1255.     return TCL_ERROR;
  1256.  
  1257.   /*
  1258.    *  Check the button binding for valid range (0 or 1-5)
  1259.    */
  1260.   if (dsPtr->button < 0 || dsPtr->button > 5)
  1261.     {
  1262.       Tcl_SetResult(interp,
  1263.             "invalid button binding: should be 1-5 or 0 for no bindings",
  1264.             TCL_STATIC);
  1265.       return TCL_ERROR;
  1266.     }
  1267.   /*
  1268.    *  Set up the rejection foreground GC for the token window...
  1269.    */
  1270.   gcValues.foreground = dsPtr->rejectFg->pixel;
  1271.   gcValues.subwindow_mode = IncludeInferiors;
  1272.   gcValues.graphics_exposures = False;
  1273.   gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1274.  
  1275.   if (dsPtr->rejectSt != None)
  1276.     {
  1277.       gcValues.stipple = dsPtr->rejectSt;
  1278.       gcValues.fill_style = FillStippled;
  1279.       gcMask |= GCForeground|GCStipple|GCFillStyle;
  1280.     }
  1281.   newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1282.  
  1283.   if (dsPtr->rejectFgGC != None)
  1284.     Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1285.   dsPtr->rejectFgGC = newGC;
  1286.  
  1287.   /*
  1288.    *  Set up the rejection background GC for the token window...
  1289.    */
  1290.   gcValues.foreground = dsPtr->rejectBg->pixel;
  1291.   gcValues.subwindow_mode = IncludeInferiors;
  1292.   gcValues.graphics_exposures = False;
  1293.   gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1294.  
  1295.   newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1296.  
  1297.   if (dsPtr->rejectBgGC != None)
  1298.     Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1299.   dsPtr->rejectBgGC = newGC;
  1300.  
  1301.   /*
  1302.    *  Reset the border width in case it has changed...
  1303.    */
  1304.   if (dsPtr->tokenwin)
  1305.     Tk_SetInternalBorder(dsPtr->tokenwin, 2*dsPtr->tokenBorderWidth);
  1306.  
  1307.   return TCL_OK;
  1308. }
  1309.  
  1310. /*
  1311.  * ------------------------------------------------------------------------
  1312.  *  FindSourceHandler()
  1313.  *
  1314.  *  Looks for the requested data type of the list of handlers for the
  1315.  *  given drag&drop source.
  1316.  * ------------------------------------------------------------------------
  1317.  */
  1318. static char*
  1319. FindSourceHandler(dsPtr, dtname)
  1320.      register DD_Source *dsPtr; /* drag&drop source widget record */
  1321.      char *dtname;              /* name of requested data type */
  1322. {
  1323.   DD_SourceHndl *dsHndl;
  1324.  
  1325.   for (dsHndl=dsPtr->handlers; dsHndl; dsHndl=dsHndl->next)
  1326.     if (strcmp(dsHndl->dataType,dtname) == 0)
  1327.       return dsHndl->cmd;
  1328.  
  1329.   return NULL;
  1330. }
  1331.  
  1332. /*
  1333.  * ------------------------------------------------------------------------
  1334.  *  PutSourceHandler()
  1335.  *
  1336.  *  Looks for the requested data type of the list of handlers for the
  1337.  *  given drag&drop source.  If found, then its associated commands are
  1338.  *  changed to the given commands.  If not found, then a new handler
  1339.  *  is created.
  1340.  * ------------------------------------------------------------------------
  1341.  */
  1342. static void
  1343. PutSourceHandler(dsPtr, dtname, cmd)
  1344.      register DD_Source *dsPtr; /* drag&drop target widget record */
  1345.      char *dtname;              /* name of data type */
  1346.      char *cmd;                 /* command used to send data */
  1347. {
  1348.   DD_SourceHndl *tail = NULL;
  1349.   DD_SourceHndl *dsHndl;
  1350.  
  1351.   for (dsHndl=dsPtr->handlers; dsHndl; tail=dsHndl,dsHndl=dsHndl->next)
  1352.     if (strcmp(dsHndl->dataType,dtname) == 0)
  1353.       {
  1354.     if (*cmd == '\0')
  1355.       {
  1356.         if (tail)
  1357.           tail->next = dsHndl->next;
  1358.         else
  1359.           dsPtr->handlers = dsHndl->next;
  1360.  
  1361.         DestroySourceHandler(dsHndl);
  1362.         return;
  1363.       }
  1364.     else
  1365.       {
  1366.         if (dsHndl->cmd != NULL) {
  1367.           free(dsHndl->cmd);
  1368.         }
  1369.         dsHndl->cmd = strdup(cmd);
  1370.         return;
  1371.       }
  1372.       }
  1373.  
  1374.   if (tail)
  1375.     tail->next = CreateSourceHandler(dtname,cmd);
  1376.   else
  1377.     dsPtr->handlers = CreateSourceHandler(dtname,cmd);
  1378. }
  1379.  
  1380. /*
  1381.  * ------------------------------------------------------------------------
  1382.  *  CreateSourceHandler()
  1383.  *
  1384.  *  Creates a new source handler record and returns a pointer to it.
  1385.  * ------------------------------------------------------------------------
  1386.  */
  1387. static DD_SourceHndl*
  1388. CreateSourceHandler(dtname, cmd)
  1389.      char *dtname;              /* name of data type */
  1390.      char *cmd;                 /* command used to send data */
  1391. {
  1392.   DD_SourceHndl *retn;
  1393.   retn = (DD_SourceHndl*)malloc(sizeof(DD_SourceHndl));
  1394.  
  1395.   retn->dataType = strdup(dtname);
  1396.   retn->cmd = strdup(cmd);
  1397.   retn->next = NULL;
  1398.   return retn;
  1399. }
  1400.  
  1401. /*
  1402.  * ------------------------------------------------------------------------
  1403.  *  DestroySourceHandler()
  1404.  *
  1405.  *  Destroys a source handler record.
  1406.  * ------------------------------------------------------------------------
  1407.  */
  1408. static void
  1409. DestroySourceHandler(dsHndl)
  1410.      DD_SourceHndl *dsHndl;
  1411. {
  1412.   if (dsHndl->dataType != NULL) {
  1413.     free(dsHndl->dataType);
  1414.   }
  1415.   if (dsHndl->cmd != NULL) {
  1416.     free(dsHndl->cmd);
  1417.   }
  1418.   free((char*)dsHndl);
  1419. }
  1420.  
  1421. /*
  1422.  * ------------------------------------------------------------------------
  1423.  *  UnregSource()
  1424.  *
  1425.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1426.  *  on a registered drag&drop source widget.
  1427.  * ------------------------------------------------------------------------
  1428.  */
  1429. static void
  1430. UnregSource(cdata, eventPtr)
  1431.      ClientData cdata;   /* drag&drop registration list */
  1432.      XEvent *eventPtr;   /* event description */
  1433. {
  1434.   DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1435.   DD_RegList *ddlist = ddentry->ddlist;
  1436.   char *ddname = Tk_PathName(ddentry->tkwin);
  1437.  
  1438.   if (eventPtr->type == DestroyNotify)
  1439.     {
  1440.       DestroySourceInfo(ddlist,ddname);
  1441.       free((char*)ddentry);
  1442.     }
  1443. }
  1444.  
  1445. /*
  1446.  * ------------------------------------------------------------------------
  1447.  *  GetTargetInfo()
  1448.  *
  1449.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1450.  *  widgets.  Creates a new record if the widget name is not already
  1451.  *  registered.  Returns a pointer to the desired record.
  1452.  * ------------------------------------------------------------------------
  1453.  */
  1454. static DD_Target*
  1455. GetTargetInfo(ddlist,pathname,newEntry)
  1456.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1457.      char* pathname;      /* widget pathname for desired record */
  1458.      int* newEntry;       /* returns non-zero => new record created */
  1459. {
  1460.   DD_Target *dtPtr;
  1461.   Tcl_HashEntry *ddEntry;
  1462.  
  1463.   ddEntry = Tcl_CreateHashEntry(&ddlist->trgList, pathname, newEntry);
  1464.   if (*newEntry)
  1465.     {
  1466.       /*
  1467.        *  Initialize a data structure for the widget...
  1468.        */
  1469.       dtPtr = (DD_Target*)malloc(sizeof(DD_Target));
  1470.       dtPtr->ddlist = ddlist;
  1471.       dtPtr->display = NULL;
  1472.       dtPtr->tkwin = NULL;
  1473.       dtPtr->handlers = NULL;
  1474.  
  1475.       Tcl_SetHashValue(ddEntry, (ClientData)dtPtr);
  1476.     }
  1477.   return (DD_Target*)Tcl_GetHashValue(ddEntry);
  1478. }
  1479.  
  1480. /*
  1481.  * ------------------------------------------------------------------------
  1482.  *  DestroyTargetInfo()
  1483.  *
  1484.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1485.  *  widgets.  Destroys the record if found.
  1486.  * ------------------------------------------------------------------------
  1487.  */
  1488. static void
  1489. DestroyTargetInfo(ddlist,pathname)
  1490.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1491.      char* pathname;      /* widget pathname for desired record */
  1492. {
  1493.   DD_Target *dtPtr;
  1494.   DD_TargetHndl *dtHndl, *next;
  1495.   Tcl_HashEntry *ddEntry;
  1496.  
  1497.   ddEntry = Tcl_FindHashEntry(&ddlist->trgList, pathname);
  1498.   dtPtr = (ddEntry) ? (DD_Target*)Tcl_GetHashValue(ddEntry) : NULL;
  1499.  
  1500.   if (dtPtr)
  1501.     {
  1502.       dtHndl = dtPtr->handlers;
  1503.       while (dtHndl)
  1504.     {
  1505.       next = dtHndl->next;
  1506.       DestroyTargetHandler(dtHndl);
  1507.       dtHndl = next;
  1508.     }
  1509.       free((char*)dtPtr);
  1510.     }
  1511.   if (ddEntry) Tcl_DeleteHashEntry(ddEntry);
  1512. }
  1513.  
  1514. /*
  1515.  * ------------------------------------------------------------------------
  1516.  *  FindTargetHandler()
  1517.  *
  1518.  *  Looks for the requested data type of the list of handlers for the
  1519.  *  given drag&drop target.
  1520.  * ------------------------------------------------------------------------
  1521.  */
  1522. static char*
  1523. FindTargetHandler(dtPtr, dtname)
  1524.      register DD_Target *dtPtr; /* drag&drop target widget record */
  1525.      char *dtname;              /* name of requested data type */
  1526. {
  1527.   DD_TargetHndl *dtHndl;
  1528.  
  1529.   for (dtHndl=dtPtr->handlers; dtHndl; dtHndl=dtHndl->next)
  1530.     if (strcmp(dtHndl->dataType,dtname) == 0)
  1531.       return dtHndl->command;
  1532.  
  1533.   return NULL;
  1534. }
  1535.  
  1536. /*
  1537.  * ------------------------------------------------------------------------
  1538.  *  PutTargetHandler()
  1539.  *
  1540.  *  Looks for the requested data type of the list of handlers for the
  1541.  *  given drag&drop target.  If found, then its associated command is
  1542.  *  changed to the given command.  If not found, then a new handler
  1543.  *  is created.
  1544.  * ------------------------------------------------------------------------
  1545.  */
  1546. static void
  1547. PutTargetHandler(dtPtr, dtname, cmd)
  1548.      register DD_Target *dtPtr; /* drag&drop target widget record */
  1549.      char *dtname;              /* name of data type */
  1550.      char *cmd;                 /* command string for data type */
  1551. {
  1552.   DD_TargetHndl *tail = NULL;
  1553.   DD_TargetHndl *dtHndl;
  1554.  
  1555.   for (dtHndl=dtPtr->handlers; dtHndl; tail=dtHndl,dtHndl=dtHndl->next)
  1556.     if (strcmp(dtHndl->dataType,dtname) == 0)
  1557.       {
  1558.     if (*cmd == '\0')
  1559.       {
  1560.         if (tail)
  1561.           tail->next = dtHndl->next;
  1562.         else
  1563.           dtPtr->handlers = dtHndl->next;
  1564.  
  1565.         DestroyTargetHandler(dtHndl);
  1566.         return;
  1567.       }
  1568.     else
  1569.       {
  1570.         if (dtHndl->command != NULL) {
  1571.           free(dtHndl->command);
  1572.         }
  1573.         dtHndl->command = strdup(cmd);
  1574.         return;
  1575.       }
  1576.       }
  1577.  
  1578.   if (tail)
  1579.     tail->next = CreateTargetHandler(dtname,cmd);
  1580.   else
  1581.     dtPtr->handlers = CreateTargetHandler(dtname,cmd);
  1582.  
  1583.   /*
  1584.    *  Update handler list stored in target window property.
  1585.    */
  1586.   AddDDProp(dtPtr);
  1587. }
  1588.  
  1589. /*
  1590.  * ------------------------------------------------------------------------
  1591.  *  CreateTargetHandler()
  1592.  *
  1593.  *  Creates a new target handler record and returns a pointer to it.
  1594.  * ------------------------------------------------------------------------
  1595.  */
  1596. static DD_TargetHndl*
  1597. CreateTargetHandler(dtname, cmd)
  1598.      char *dtname;              /* name of data type */
  1599.      char *cmd;                 /* command string for data type */
  1600. {
  1601.   DD_TargetHndl *retn;
  1602.   retn = (DD_TargetHndl*)malloc(sizeof(DD_TargetHndl));
  1603.  
  1604.   retn->dataType = strdup(dtname);
  1605.   retn->command = strdup(cmd);
  1606.  
  1607.   retn->next = NULL;
  1608.   return retn;
  1609. }
  1610.  
  1611. /*
  1612.  * ------------------------------------------------------------------------
  1613.  *  DestroyTargetHandler()
  1614.  *
  1615.  *  Destroys a target handler record.
  1616.  * ------------------------------------------------------------------------
  1617.  */
  1618. static void
  1619. DestroyTargetHandler(dtHndl)
  1620.      DD_TargetHndl *dtHndl;
  1621. {
  1622.   if (dtHndl->dataType != NULL) {
  1623.     free(dtHndl->dataType);
  1624.   }
  1625.   if (dtHndl->command != NULL) {
  1626.     free(dtHndl->command);
  1627.   }
  1628.   free((char*)dtHndl);
  1629. }
  1630.  
  1631. /*
  1632.  * ------------------------------------------------------------------------
  1633.  *  UnregTarget()
  1634.  *
  1635.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1636.  *  on a registered drag&drop target widget.
  1637.  * ------------------------------------------------------------------------
  1638.  */
  1639. static void
  1640. UnregTarget(cdata, eventPtr)
  1641.      ClientData cdata;   /* drag&drop registration list */
  1642.      XEvent *eventPtr;   /* event description */
  1643. {
  1644.   DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1645.   DD_RegList *ddlist = ddentry->ddlist;
  1646.   char *ddname = Tk_PathName(ddentry->tkwin);
  1647.  
  1648.   if (eventPtr->type == DestroyNotify)
  1649.     {
  1650.       DestroyTargetInfo(ddlist,ddname);
  1651.       free((char*)ddentry);
  1652.     }
  1653. }
  1654.  
  1655. /*
  1656.  * ------------------------------------------------------------------------
  1657.  *  DragDropSend()
  1658.  *
  1659.  *  Invoked after a drop operation to send data to the drop application.
  1660.  * ------------------------------------------------------------------------
  1661.  */
  1662. static void
  1663. DragDropSend(dsPtr)
  1664.      register DD_Source *dsPtr;  /* drag&drop source record */
  1665. {
  1666.   DD_RegList *ddlist = dsPtr->ddlist;
  1667.  
  1668.   int status;
  1669.   char *sendcmd;
  1670.   DD_WinRep *target;
  1671.  
  1672.   /*
  1673.    *  See if current position is over drop point...
  1674.    */
  1675.   target = FindTargetWin(dsPtr, dsPtr->tokenx,dsPtr->tokeny);
  1676.  
  1677.   if (target)
  1678.     {
  1679.       char buffer[256];
  1680.  
  1681.       sprintf(buffer, "%d %d", dsPtr->tokenx, dsPtr->tokeny);
  1682.       status = Tcl_VarEval(ddlist->interp,
  1683.                "send {",target->ddinterp,"} ", DRAGDROP_COMMAND," location ",buffer,
  1684.                (char*)NULL);
  1685.  
  1686.       if (status == TCL_OK)
  1687.     {
  1688.       sendcmd = DragDropSendHndlr(dsPtr, target->ddinterp, target->ddwin);
  1689.       if (sendcmd)
  1690.         {
  1691.           status = Tcl_VarEval(ddlist->interp,
  1692.                    sendcmd, " {",target->ddinterp,"} {",target->ddwin,
  1693.                    "} {",dsPtr->pkgcmdResult,"}",
  1694.                    (char*)NULL);
  1695.         }
  1696.       else
  1697.         {
  1698.           Tcl_AppendResult(ddlist->interp, "target \"",
  1699.                    target->ddwin,
  1700.                    "\" does not recognize handlers for source \"",
  1701.                    Tk_PathName(dsPtr->tkwin), "\"",
  1702.                    (char*)NULL);
  1703.           status = TCL_ERROR;
  1704.         }
  1705.     }
  1706.  
  1707.       /*
  1708.        *  Give success/failure feedback to user.
  1709.        *  If an error occurred and an error proc is defined,
  1710.        *  then use it to handle the error.
  1711.        */
  1712.       if (status == TCL_OK)
  1713.     HideDDToken((ClientData)dsPtr);
  1714.       else
  1715.     {
  1716.       RejectDDToken(dsPtr);
  1717.  
  1718.       if (ddlist->errorProc && *ddlist->errorProc)
  1719.         (void) Tcl_VarEval(ddlist->interp,
  1720.                    ddlist->errorProc, " {", ddlist->interp->result, "}",
  1721.                    (char*)NULL);
  1722.     }
  1723.     }
  1724. }
  1725.  
  1726. /*
  1727.  * ------------------------------------------------------------------------
  1728.  *  DragDropSendHndlr()
  1729.  *
  1730.  *  Queries the drag&drop target under the specified interpreter for a
  1731.  *  handler that is compatible with one of the handlers defined for the
  1732.  *  source.  Returns a pointer to the appropriate send command, or
  1733.  *  NULL if none is found.
  1734.  * ------------------------------------------------------------------------
  1735.  */
  1736. static char*
  1737. DragDropSendHndlr(dsPtr,interpName,ddName)
  1738.      register DD_Source *dsPtr;  /* drag&drop source record */
  1739.      char *interpName;           /* interpreter containing drag&drop target */
  1740.      char *ddName;               /* drag&drop target pathname */
  1741. {
  1742.   char *retn = NULL;        /* no handler found yet */
  1743.   Tcl_Interp *interp = dsPtr->ddlist->interp;
  1744.  
  1745.   int hndlc, hi, ei;
  1746.   char **hndlv, *hlist;
  1747.   DD_SourceHndl *dsHndl;
  1748.   char buffer[1024];
  1749.  
  1750.   /*
  1751.    *  Query the drag&drop target for its list of known handlers.
  1752.    */
  1753.   Tcl_ResetResult(interp);    /* for Tcl_AppendResult() below */
  1754.   if (Tcl_VarEval(interp,
  1755.           "send {",interpName,"} ", DRAGDROP_COMMAND," target {",ddName,"} handler",
  1756.           (char*)NULL) != TCL_OK)
  1757.     return NULL;
  1758.  
  1759.   hlist = strdup(interp->result);
  1760.   if (Tcl_SplitList(interp, hlist, &hndlc, &hndlv) == TCL_OK)
  1761.     {
  1762.       /*
  1763.        *  If the list of send handlers is specified as "all", then
  1764.        *  search through the handlers in order.
  1765.        */
  1766.       if (strcmp(dsPtr->send,"all") == 0)
  1767.     for (dsHndl=dsPtr->handlers; dsHndl && !retn; dsHndl=dsHndl->next)
  1768.       {
  1769.         for (hi=0; (hi < hndlc) && !retn; hi++)
  1770.           if (strcmp(dsHndl->dataType, hndlv[hi]) == 0)
  1771.         retn = dsHndl->cmd;
  1772.       }
  1773.  
  1774.       /*
  1775.        *  Otherwise, search through the specified send handlers.
  1776.        */
  1777.       else
  1778.     {
  1779.       int elemc;
  1780.       char **elemv;
  1781.       if (Tcl_SplitList(interp,dsPtr->send,&elemc,&elemv)==TCL_OK)
  1782.         {
  1783.           for (ei=0; (ei < elemc) && !retn; ei++)
  1784.         for (hi=0; (hi < hndlc) && !retn; hi++)
  1785.           if (strcmp(elemv[ei], hndlv[hi]) == 0)
  1786.             {
  1787.               retn = FindSourceHandler(dsPtr, elemv[ei]);
  1788.               if (!retn)
  1789.             {
  1790.               sprintf(buffer, "unknown handler \"%.50s\" requested for drag&drop source \"%.200s\"", elemv[ei], Tk_PathName(dsPtr->tkwin));
  1791.               Tcl_ResetResult(interp);
  1792.               Tcl_AddErrorInfo(interp, buffer);
  1793.               Tk_BackgroundError(interp);
  1794.             }
  1795.             }
  1796.  
  1797.           free((char*)elemv);
  1798.         }
  1799.       else
  1800.         {
  1801.           sprintf(buffer, "drag&drop source has invalid -send: %.200s",
  1802.               dsPtr->send);
  1803.           Tcl_ResetResult(interp);
  1804.           Tcl_AddErrorInfo(interp, buffer);
  1805.           Tk_BackgroundError(interp);
  1806.         }
  1807.     }
  1808.       free((char*)hndlv);
  1809.     }
  1810.   free(hlist);
  1811.  
  1812.   return retn;
  1813. }
  1814.  
  1815.  
  1816. /*
  1817.  * ------------------------------------------------------------------------
  1818.  *  GetWinRepInfo()
  1819.  *
  1820.  *  Invoked at the start of a "drag" operation to capture the positions
  1821.  *  of all windows on the current root.  Queries the entire window
  1822.  *  hierarchy and determines the placement of each window.  Queries
  1823.  *  "DragDropInfo" property info where appropriate.  This information
  1824.  *  is used during the drag operation to determine when the drag&drop
  1825.  *  token is over a valid drag&drop target.
  1826.  *
  1827.  *  Returns the record for the root window, which contains records for
  1828.  *  all other windows as children.
  1829.  * ------------------------------------------------------------------------
  1830.  */
  1831. static DD_WinRep*
  1832. GetWinRepInfo(dsPtr,ddlist)
  1833.      DD_Source *dsPtr;     /* drag&drop source window */
  1834.      DD_RegList *ddlist;   /* drag&drop registration info */
  1835. {
  1836.   DD_WinRep *wr;
  1837.  
  1838.   wr = WinRepAlloc(ddlist);
  1839.   wr->win = DefaultRootWindow(dsPtr->display);
  1840.   WinRepInit(wr, dsPtr);
  1841.  
  1842.   return wr;
  1843. }
  1844.  
  1845. /*
  1846.  * ------------------------------------------------------------------------
  1847.  *  FindTargetWin()
  1848.  *
  1849.  *  Checks to see if a compatible drag&drop target exists at the given
  1850.  *  position.  A target is "compatible" if it is a drag&drop window,
  1851.  *  and if it has a handler that is compatible with the current source
  1852.  *  window.
  1853.  *
  1854.  *  Returns a pointer to a structure describing the target, or NULL
  1855.  *  if no compatible target is found.
  1856.  * ------------------------------------------------------------------------
  1857.  */
  1858. static DD_WinRep*
  1859. FindTargetWin(dsPtr,x,y)
  1860.      DD_Source *dsPtr;     /* drag&drop source window */
  1861.      int x,y;              /* current drag&drop location (virtual coords) */
  1862. {
  1863.   int vx, vy, vw, vh;
  1864.   register char *type;
  1865.  
  1866.   DD_WinRep *wr, *wrkid;
  1867.   DD_Stack stack;
  1868.   DD_SourceHndl *shandl;
  1869.  
  1870.   /*
  1871.    *  If window representations have not yet been built, then
  1872.    *  abort this call.  This probably means that the token is being
  1873.    *  moved before it has been properly built.
  1874.    */
  1875.   if (!dsPtr->allwins)
  1876.     return NULL;
  1877.  
  1878.   /*
  1879.    *  Adjust current location for virtual root windows.
  1880.    */
  1881.   Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  1882.   x += vx;
  1883.   y += vy;
  1884.  
  1885.   /*
  1886.    *  Build a stack of all windows containing the given point,
  1887.    *  in order from least to most specific.
  1888.    */
  1889.   StackInit(&stack);
  1890.  
  1891.   wr = dsPtr->allwins;
  1892.   if ((x >= wr->x0) && (x <= wr->x1) &&
  1893.       (y >= wr->y0) && (y <= wr->y1))
  1894.     StackPush((ClientData)wr, &stack);
  1895.  
  1896.   while (wr)
  1897.     {
  1898.       for (wrkid=wr->kids; wrkid; wrkid=wrkid->next)
  1899.     {
  1900.       if (!wrkid->initialized)
  1901.         WinRepInit(wrkid, dsPtr);
  1902.  
  1903.       if ((x >= wrkid->x0) && (x <= wrkid->x1) &&
  1904.           (y >= wrkid->y0) && (y <= wrkid->y1))
  1905.         {
  1906.           StackPush((ClientData)wrkid, &stack);
  1907.           break;
  1908.         }
  1909.     }
  1910.       wr = wrkid;        /* continue search */
  1911.     }
  1912.  
  1913.   /*
  1914.    *  Pop windows from the stack until one containing a
  1915.    *  "DragDropInfo" property is found.  See if the handlers
  1916.    *  listed in this property are compatible with the
  1917.    *  given source.
  1918.    */
  1919.   while ((wr=(DD_WinRep*)StackPop(&stack)) != NULL)
  1920.     if (wr->ddprop)
  1921.       break;
  1922.  
  1923.   if (wr && wr->ddhandlers)
  1924.     {
  1925.       type = wr->ddhandlers;
  1926.       while (*type != '\0')
  1927.     {
  1928.       for (shandl=dsPtr->handlers; shandl; shandl=shandl->next)
  1929.         if (strcmp(shandl->dataType, type) == 0)
  1930.           break;
  1931.  
  1932.       if (shandl)        /* found a match? */
  1933.         break;        /* then stop searching */
  1934.       else            /* otherwise, move to next handler type */
  1935.         {
  1936.           while (*type++ != '\0')
  1937.         ;
  1938.         }
  1939.     }
  1940.       if (*type == '\0')    /* no handler match? */
  1941.     wr = NULL;        /* then return NULL */
  1942.     }
  1943.   StackDelete(&stack);
  1944.  
  1945.   return wr;
  1946. }
  1947.  
  1948.  
  1949. /*
  1950.  * ------------------------------------------------------------------------
  1951.  *  WinRepAlloc()
  1952.  *
  1953.  *  Returns a new structure for representing X window position info.
  1954.  *  Such structures are typically allocated at the start of a drag&drop
  1955.  *  operation to capture the placement of all windows on the root
  1956.  *  window.  The drag&drop registration list keeps a pool of such
  1957.  *  structures so that they can be allocated quickly when needed.
  1958.  *  Returns a pointer to an empty structure.
  1959.  * ------------------------------------------------------------------------
  1960.  */
  1961. static DD_WinRep*
  1962. WinRepAlloc(ddlist)
  1963.      DD_RegList *ddlist;  /* drag&drop registration list */
  1964. {
  1965.   DD_WinRep *wr;
  1966.  
  1967.   /*
  1968.    *  Return the top-most structure in the pool.
  1969.    *  If the pool is empty, add a new structure to it.
  1970.    */
  1971.   if (!ddlist->pool)
  1972.     {
  1973.       wr = (DD_WinRep*)malloc(sizeof(DD_WinRep));
  1974.       wr->next = NULL;
  1975.       ddlist->pool = wr;
  1976.     }
  1977.   wr = ddlist->pool;
  1978.   ddlist->pool = wr->next;
  1979.  
  1980.   wr->initialized = 0;
  1981.   wr->ddprop = NULL;
  1982.   wr->ddinterp = wr->ddwin = wr->ddhandlers = NULL;
  1983.   wr->parent = wr->kids = wr->next = NULL;
  1984.   return wr;
  1985. }
  1986.  
  1987. /*
  1988.  * ------------------------------------------------------------------------
  1989.  *  WinRepRelease()
  1990.  *
  1991.  *  Puts a window representation structure back into the global pool,
  1992.  *  making it available for future calls to WinRepAlloc().  Any
  1993.  *  associated resources (within the structure) are automatically freed.
  1994.  * ------------------------------------------------------------------------
  1995.  */
  1996. static void
  1997. WinRepRelease(wr,ddlist)
  1998.      DD_WinRep *wr;       /* window rep to be freed */
  1999.      DD_RegList *ddlist;  /* drag&drop registration list */
  2000. {
  2001.   DD_WinRep *wrkid, *wrnext;
  2002.  
  2003.   for (wrkid=wr->kids; wrkid; wrkid=wrnext)
  2004.     {
  2005.       wrnext = wrkid->next;
  2006.       WinRepRelease(wrkid,ddlist);
  2007.     }
  2008.  
  2009.   if (wr->ddprop)
  2010.     XFree(wr->ddprop);
  2011.  
  2012.   wr->next = ddlist->pool;    /* put back into pool */
  2013.   ddlist->pool = wr;
  2014. }
  2015.  
  2016.  
  2017. /*
  2018.  * ------------------------------------------------------------------------
  2019.  *  WinRepInit()
  2020.  *
  2021.  *  Invoked during "drag" operations to dig a little deeper into the
  2022.  *  root window hierarchy and cache the resulting information.  If a
  2023.  *  point coordinate lies within an uninitialized DD_WinRep, this
  2024.  *  routine is called to query window coordinates and drag&drop info.
  2025.  *  If this particular window has any children, more uninitialized
  2026.  *  DD_WinRep structures are allocated.  Further queries will cause
  2027.  *  these structures to be initialized in turn.
  2028.  * ------------------------------------------------------------------------
  2029.  */
  2030. static void
  2031. WinRepInit(wr,dsPtr)
  2032.      DD_WinRep *wr;         /* window rep to be initialized */
  2033.      DD_Source *dsPtr;      /* drag&drop source managing win rep */
  2034. {
  2035.   Window ignoreSource = Tk_WindowId(dsPtr->tkwin);
  2036.   Window ignoreToken  = Tk_WindowId(dsPtr->tokenwin);
  2037.  
  2038.   DD_WinRep *wrkid, *wrtail;
  2039.  
  2040.   Window root, parent, *kids;
  2041.   unsigned int nkids;
  2042.   XWindowAttributes winInfo;
  2043.  
  2044.   char *propInfo;
  2045.   int i, result, actualFormat;
  2046.   Atom actualType;
  2047.   unsigned long numItems, bytesAfter;
  2048.  
  2049.   /*
  2050.    *  If the self-target flag is set, allow the source window to
  2051.    *  drop onto itself.  Do not ignore source window during search.
  2052.    */
  2053.   if (dsPtr->selfTarget)
  2054.     ignoreSource = None;
  2055.  
  2056.   if (!wr->initialized)
  2057.     {
  2058.       /*
  2059.        *  Query for the window coordinates.
  2060.        */
  2061.       if (XGetWindowAttributes(dsPtr->display, wr->win, &winInfo) &&
  2062.       (winInfo.map_state == IsViewable) &&
  2063.       (wr->win != ignoreToken) &&
  2064.       (wr->win != ignoreSource))
  2065.     {
  2066.       wr->x0  = winInfo.x;
  2067.       wr->y0  = winInfo.y;
  2068.       wr->x1  = winInfo.x + winInfo.width;
  2069.       wr->y1  = winInfo.y + winInfo.height;
  2070.  
  2071.       if (wr->parent)    /* offset by parent coords */
  2072.         {
  2073.           wr->x0 += wr->parent->x0;
  2074.           wr->y0 += wr->parent->y0;
  2075.           wr->x1 += wr->parent->x0;
  2076.           wr->y1 += wr->parent->y0;
  2077.         }
  2078.     }
  2079.       else
  2080.     {
  2081.       wr->x0 = wr->y0 = -1;
  2082.       wr->x1 = wr->y1 = -1;
  2083.     }
  2084.  
  2085.       /*
  2086.        *  See if this window has a "DragDropInfo" property.
  2087.        */
  2088.       result = XGetWindowProperty(dsPtr->display, wr->win,
  2089.                   dsPtr->ddAtom, 0, MAX_PROP_SIZE, False, XA_STRING,
  2090.                   &actualType, &actualFormat,
  2091.                   &numItems, &bytesAfter, (unsigned char**)&propInfo);
  2092.  
  2093.       if ((result != Success) ||
  2094.       (actualFormat != 8) ||
  2095.       (actualType != XA_STRING))
  2096.     {
  2097.       if (propInfo != NULL) {
  2098.         XFree((caddr_t)propInfo);
  2099.       }
  2100.       propInfo = NULL;
  2101.     }
  2102.  
  2103.       wr->ddprop = propInfo;
  2104.       if (wr->ddprop)
  2105.     {
  2106.       char *p = wr->ddprop;
  2107.       wr->ddinterp = wr->ddprop;
  2108.  
  2109.       while ((*p != '\0') && (*p != ']'))
  2110.         p++;
  2111.  
  2112.       if (*p != '\0')
  2113.         {
  2114.           *p++ = '\0';    /* terminate interp name */
  2115.           wr->ddwin = p;    /* get start of window name */
  2116.         }
  2117.  
  2118.       while ((*p != '\0') && (*p != ']'))
  2119.         p++;
  2120.  
  2121.       if (*p != '\0')
  2122.         {
  2123.           *p++ = '\0';    /* terminate window name */
  2124.           wr->ddhandlers = p; /* get start of handler list */
  2125.  
  2126.           /*
  2127.            *  Handler strings are of the form:
  2128.            *  "<type> <type> ... <type> "
  2129.            */
  2130.           while (*p != '\0')
  2131.         {
  2132.           while ((*p != ' ') && (*p != '\0'))
  2133.             p++;
  2134.  
  2135.           *p++ = '\0';  /* null terminate handler type */
  2136.         }
  2137.         }
  2138.     }
  2139.  
  2140.       /*
  2141.        *  If this window has any children, then create DD_WinReps
  2142.        *  for them as well.
  2143.        */
  2144.       if (XQueryTree(dsPtr->display, wr->win, &root, &parent, &kids, &nkids))
  2145.     {
  2146.       wrtail = NULL;
  2147.       for (i=nkids-1; i >= 0; i--)
  2148.         {
  2149.           wrkid = WinRepAlloc(dsPtr->ddlist);
  2150.           wrkid->win = kids[i];
  2151.           wrkid->parent = wr;
  2152.  
  2153.           if (wrtail)
  2154.         wrtail->next = wrkid;
  2155.           else
  2156.         wr->kids = wrkid;
  2157.  
  2158.           wrtail = wrkid;
  2159.         }
  2160.       if (kids != NULL) {
  2161.         XFree((caddr_t)kids); /* done with list of kids */
  2162.       }
  2163.     }
  2164.     }
  2165.   wr->initialized = ~0;
  2166. }
  2167.  
  2168.  
  2169. /*
  2170.  * ------------------------------------------------------------------------
  2171.  *  AddDDProp()
  2172.  *
  2173.  *  Attaches a "DragDropInfo" property to the given target window.  This
  2174.  *  property allows the drag&drop mechanism to recognize the window as
  2175.  *  a valid target, and stores important information including the
  2176.  *  interpreter managing the target and the pathname for the target
  2177.  *  window.  Usually invoked when the target is first registered or
  2178.  *  first exposed (so that the X-window really exists).
  2179.  * ------------------------------------------------------------------------
  2180.  */
  2181. static void
  2182. AddDDProp(dtPtr)
  2183.      DD_Target* dtPtr;  /* drag&drop target window data */
  2184. {
  2185.   Tcl_Interp *interp = dtPtr->ddlist->interp;
  2186.  
  2187.   Atom ddProperty;
  2188.   char buffer[MAX_PROP_SIZE], *path, *info;
  2189.   DD_TargetHndl *thandl;
  2190.  
  2191.   if (dtPtr->tkwin != None)
  2192.     {
  2193.       static char command[] = { "winfo name ." };
  2194.  
  2195.       path = Tk_PathName(dtPtr->tkwin);
  2196.       if (Tcl_Eval(interp, command)==TCL_OK)
  2197.     sprintf(buffer, "%s]%s]", interp->result, path);
  2198.       else
  2199.     sprintf(buffer, "]%s]", path);
  2200.  
  2201.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  2202.       for (thandl=dtPtr->handlers; thandl; thandl=thandl->next)
  2203.     Tcl_AppendResult(interp, thandl->dataType, " ", (char*)NULL);
  2204.  
  2205.       ddProperty = XInternAtom(dtPtr->display, DRAGDROP_PROPINFO, False);
  2206.       info = interp->result;
  2207.  
  2208.       XChangeProperty(dtPtr->display, Tk_WindowId(dtPtr->tkwin),
  2209.               ddProperty, XA_STRING, 8, PropModeReplace,
  2210.               (unsigned char*)info, strlen(info)+1);
  2211.     }
  2212. }
  2213.  
  2214. /*
  2215.  * ------------------------------------------------------------------------
  2216.  *  DDTokenEventProc()
  2217.  *
  2218.  *  Invoked by the Tk dispatcher to handle widget events.
  2219.  *  Manages redraws for the drag&drop token window.
  2220.  * ------------------------------------------------------------------------
  2221.  */
  2222. static void
  2223. DDTokenEventProc(clientData, eventPtr)
  2224.      ClientData clientData;     /* data associated with widget */
  2225.      XEvent *eventPtr;          /* information about event */
  2226. {
  2227.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2228.  
  2229.   if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  2230.     {
  2231.       if (dsPtr->tokenwin)
  2232.     {
  2233.       Tk_Window tkwin = dsPtr->tokenwin;
  2234.       int bd;
  2235.  
  2236.       bd = dsPtr->tokenBorderWidth;
  2237.  
  2238.       Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  2239.                  dsPtr->tokenOutline,
  2240.                  0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2241.                  0, TK_RELIEF_FLAT);
  2242.       Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  2243.                  dsPtr->tokenBorder,
  2244.                  bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2245.                  bd, (dsPtr->overTargetWin) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2246.     }
  2247.     }
  2248.   else if (eventPtr->type == DestroyNotify)
  2249.     dsPtr->tokenwin = NULL;
  2250. }
  2251.  
  2252. /*
  2253.  * ------------------------------------------------------------------------
  2254.  *  MoveDDToken()
  2255.  *
  2256.  *  Invoked during "drag" operations to move a token window to its
  2257.  *  current "drag" coordinate.
  2258.  * ------------------------------------------------------------------------
  2259.  */
  2260. static void
  2261. MoveDDToken(dsPtr)
  2262.      DD_Source *dsPtr;  /* drag&drop source window data */
  2263. {
  2264.   int x = dsPtr->tokenx;
  2265.   int y = dsPtr->tokeny;
  2266.   Tk_Window tokenwin = dsPtr->tokenwin;
  2267.  
  2268.   int max;
  2269.   int vx, vy, vw, vh;
  2270.  
  2271.   /*
  2272.    *  Adjust current location for virtual root windows.
  2273.    */
  2274.   Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  2275.   x += vx;
  2276.   y += vy;
  2277.  
  2278.   max = WidthOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Width(tokenwin);
  2279.   switch (dsPtr->tokenAnchor)
  2280.     {
  2281.     case TK_ANCHOR_NW:
  2282.     case TK_ANCHOR_W:
  2283.     case TK_ANCHOR_SW:
  2284.       break;
  2285.  
  2286.     case TK_ANCHOR_N:
  2287.     case TK_ANCHOR_CENTER:
  2288.     case TK_ANCHOR_S:
  2289.       x -= Tk_Width(tokenwin)/2;
  2290.       break;
  2291.  
  2292.     case TK_ANCHOR_NE:
  2293.     case TK_ANCHOR_E:
  2294.     case TK_ANCHOR_SE:
  2295.       x -= Tk_Width(tokenwin);
  2296.       break;
  2297.     }
  2298.   if (x > max) {
  2299.     x = max;
  2300.   } else if (x < 0) {
  2301.     x = 0;
  2302.   }
  2303.   max = HeightOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Height(tokenwin);
  2304.   switch (dsPtr->tokenAnchor)
  2305.     {
  2306.     case TK_ANCHOR_NW:
  2307.     case TK_ANCHOR_N:
  2308.     case TK_ANCHOR_NE:
  2309.       break;
  2310.  
  2311.     case TK_ANCHOR_W:
  2312.     case TK_ANCHOR_CENTER:
  2313.     case TK_ANCHOR_E:
  2314.       y -= Tk_Height(tokenwin)/2;
  2315.       break;
  2316.  
  2317.     case TK_ANCHOR_SW:
  2318.     case TK_ANCHOR_S:
  2319.     case TK_ANCHOR_SE:
  2320.       y -= Tk_Height(tokenwin);
  2321.       break;
  2322.     }
  2323.   if (y > max) {
  2324.     y = max;
  2325.   } else if (y < 0) {
  2326.     y = 0;
  2327.   }
  2328.   if ((x != Tk_X(tokenwin)) || (y != Tk_Y(tokenwin)))
  2329.     Tk_MoveWindow(tokenwin, x, y);
  2330. }
  2331.  
  2332. /*
  2333.  * ------------------------------------------------------------------------
  2334.  *  UpdateDDToken()
  2335.  *
  2336.  *  Invoked when the event loop is idle to determine whether or not
  2337.  *  the current drag&drop token position is over another drag&drop
  2338.  *  target.
  2339.  * ------------------------------------------------------------------------
  2340.  */
  2341. static void
  2342. UpdateDDToken(clientData)
  2343.      ClientData clientData;  /* widget data */
  2344. {
  2345.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2346.   Tk_Window tkwin = dsPtr->tokenwin;
  2347.  
  2348.   int status, bd;
  2349.  
  2350.   status = (FindTargetWin(dsPtr, dsPtr->tokenx, dsPtr->tokeny) != NULL);
  2351.  
  2352.   bd = dsPtr->tokenBorderWidth;
  2353.   if (dsPtr->overTargetWin ^ status)
  2354.     {
  2355.       Tk_Fill3DRectangle(dsPtr->tkwin, Tk_WindowId(tkwin),
  2356.              dsPtr->tokenOutline,
  2357.              0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2358.              0, TK_RELIEF_FLAT);
  2359.       Tk_Fill3DRectangle(dsPtr->tkwin, Tk_WindowId(tkwin),
  2360.              dsPtr->tokenBorder,
  2361.              bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2362.              bd, (status) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2363.  
  2364.       /*
  2365.        *  If the source has a site command, then invoke it to
  2366.        *  modify the appearance of the token window.  Pass any
  2367.        *  errors onto the drag&drop error handler.
  2368.        */
  2369.       if (dsPtr->sitecmd)
  2370.     {
  2371.       char buffer[200];
  2372.  
  2373.       sprintf(buffer, "%d", status);
  2374.       if ((Tcl_VarEval(dsPtr->ddlist->interp,
  2375.                dsPtr->sitecmd," ",buffer," ",Tk_PathName(dsPtr->tokenwin),
  2376.                (char*)NULL) != TCL_OK) &&
  2377.           dsPtr->ddlist->errorProc && *dsPtr->ddlist->errorProc)
  2378.  
  2379.         (void) Tcl_VarEval(dsPtr->ddlist->interp,
  2380.                    dsPtr->ddlist->errorProc, " {",
  2381.                    dsPtr->ddlist->interp->result, "}",
  2382.                    (char*)NULL);
  2383.     }
  2384.     }
  2385.   dsPtr->overTargetWin = status;
  2386. }
  2387.  
  2388. /*
  2389.  * ------------------------------------------------------------------------
  2390.  *  HideDDToken()
  2391.  *
  2392.  *  Unmaps the drag&drop token.  Invoked directly at the end of a
  2393.  *  successful communication, or after a delay if the communication
  2394.  *  fails (allowing the user to see a graphical picture of failure).
  2395.  * ------------------------------------------------------------------------
  2396.  */
  2397. static void
  2398. HideDDToken(clientData)
  2399.      ClientData clientData;  /* widget data */
  2400. {
  2401.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2402.  
  2403.   if (dsPtr->tokenwin)
  2404.     Tk_UnmapWindow(dsPtr->tokenwin);
  2405.  
  2406.   dsPtr->hidetoken = NULL;
  2407. }
  2408.  
  2409. /*
  2410.  * ------------------------------------------------------------------------
  2411.  *  RejectDDToken()
  2412.  *
  2413.  *  Draws a rejection mark on the current drag&drop token, and arranges
  2414.  *  for the token to be unmapped after a small delay.
  2415.  * ------------------------------------------------------------------------
  2416.  */
  2417. static void
  2418. RejectDDToken(dsPtr)
  2419.      DD_Source* dsPtr;  /* widget data */
  2420. {
  2421.   int div = 6;            /* controls size of rejection symbol */
  2422.   int w,h,lwid,x,y,margin;
  2423.  
  2424.   Tk_Window tkwin = dsPtr->tokenwin;
  2425.  
  2426.   margin = 2*dsPtr->tokenBorderWidth;
  2427.   w = Tk_Width(tkwin) - 2*margin;
  2428.   h = Tk_Height(tkwin) - 2*margin;
  2429.  
  2430.   lwid = (w < h) ? w/div : h/div;
  2431.   lwid = (lwid < 1) ? 1 : lwid;
  2432.  
  2433.   w = h = lwid*(div-1);
  2434.   x = (Tk_Width(tkwin) - w)/2;
  2435.   y = (Tk_Height(tkwin) - h)/2;
  2436.  
  2437.   /*
  2438.    *  Draw the rejection symbol background (\) on the token window...
  2439.    */
  2440.   XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectBgGC,
  2441.              lwid+4, LineSolid, CapButt, JoinBevel);
  2442.  
  2443.   XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2444.        x, y, w, h, 0, 23040);
  2445.  
  2446.   XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2447.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2448.  
  2449.   /*
  2450.    *  Draw the rejection symbol foreground (\) on the token window...
  2451.    */
  2452.   XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectFgGC,
  2453.              lwid, LineSolid, CapButt, JoinBevel);
  2454.  
  2455.   XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2456.        x, y, w, h, 0, 23040);
  2457.  
  2458.   XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2459.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2460.  
  2461.   /*
  2462.    *  Arrange for token window to disappear eventually.
  2463.    */
  2464.   dsPtr->hidetoken
  2465.     = Tk_CreateTimerHandler(1000, HideDDToken, (ClientData)dsPtr);
  2466. }
  2467.  
  2468.  
  2469. /*
  2470.  * ------------------------------------------------------------------------
  2471.  *  StackInit()
  2472.  *
  2473.  *  Initializes a stack structure, allocating a certain amount of memory
  2474.  *  for the stack and setting the stack length to zero.
  2475.  * ------------------------------------------------------------------------
  2476.  */
  2477. static void
  2478. StackInit(stack)
  2479.      DD_Stack *stack;     /* stack to be initialized */
  2480. {
  2481.   stack->values = stack->space;
  2482.   stack->max = sizeof(stack->space)/sizeof(ClientData);
  2483.   stack->len = 0;
  2484. }
  2485.  
  2486. /*
  2487.  * ------------------------------------------------------------------------
  2488.  *  StackDelete()
  2489.  *
  2490.  *  Destroys a stack structure, freeing any memory that may have been
  2491.  *  allocated to represent it.
  2492.  * ------------------------------------------------------------------------
  2493.  */
  2494. static void
  2495. StackDelete(stack)
  2496.      DD_Stack *stack;     /* stack to be deleted */
  2497. {
  2498.   if (stack->values != stack->space) /* allocated extra memory? */
  2499.     free((char*)stack->values);    /* then free it */
  2500.  
  2501.   stack->values = NULL;
  2502.   stack->len = stack->max = 0;
  2503. }
  2504.  
  2505. /*
  2506.  * ------------------------------------------------------------------------
  2507.  *  StackPush()
  2508.  *
  2509.  *  Pushes a piece of client data onto the top of the given stack.
  2510.  * ------------------------------------------------------------------------
  2511.  */
  2512. static void
  2513. StackPush(cdata,stack)
  2514.      ClientData cdata;    /* data to be pushed onto stack */
  2515.      DD_Stack *stack;     /* stack */
  2516. {
  2517.   ClientData *newStack;
  2518.  
  2519.   if (stack->len+1 >= stack->max)
  2520.     {
  2521.       stack->max = (stack->max == 0) ? 5 : 2*stack->max;
  2522.       newStack = (ClientData*)
  2523.     malloc((unsigned)(stack->max*sizeof(ClientData)));
  2524.  
  2525.       if (stack->values)
  2526.     {
  2527.       memcpy((char *)newStack, (char *)stack->values,
  2528.          stack->len*sizeof(ClientData));
  2529.  
  2530.       if (stack->values != stack->space)
  2531.         free((char*)stack->values);
  2532.     }
  2533.       stack->values = newStack;
  2534.     }
  2535.   stack->values[stack->len++] = cdata;
  2536. }
  2537.  
  2538. /*
  2539.  * ------------------------------------------------------------------------
  2540.  *  StackPop()
  2541.  *
  2542.  *  Pops a bit of client data from the top of the given stack.
  2543.  * ------------------------------------------------------------------------
  2544.  */
  2545. static ClientData
  2546. StackPop(stack)
  2547.      DD_Stack *stack;  /* stack to be manipulated */
  2548. {
  2549.   if ((stack->values != NULL) && (stack->len > 0)) {
  2550.     return (stack->values[--stack->len]);
  2551.   }
  2552.   return (ClientData) 0;
  2553. }
  2554.